package com.gitee.wsl.collections.set

import com.gitee.wsl.base.Objects.equals
import com.gitee.wsl.collections.set.api.MultiSet
import com.gitee.wsl.text.format.format
import kotlin.jvm.Transient

/**
 * Abstract implementation of the [MultiSet] interface to simplify the
 * creation of subclass implementations.
 *
 * @param <E> the type held in the multiset
 * @since 4.1
</E> */
abstract class AbstractMultiSet<E> protected constructor() : AbstractCollection<E>(), MultiSet<E> {
    /**
     * Inner class AbstractEntry.
     *
     * @param <E> the element type.
    </E> */
    protected abstract class AbstractEntry<E> : MultiSet.Entry<E> {
        override fun equals(other: Any?): Boolean {
            if (other is MultiSet.Entry<*>) {
                val other = other
                val element = element
                val otherElement: Any? = other.element

                return this.count == other.count && equals<Any?>(element, otherElement)
            }
            return false
        }

        override fun hashCode(): Int {
            val element = element
            return (element?.hashCode() ?: 0) xor count
        }

        override fun toString(): String {
            return String.format("%s:%d", element, count)
        }
    }

    /**
     * Inner class EntrySet.
     *
     * @param <E> the element type.
    </E> */
    protected class EntrySet<E>(private val parent: AbstractMultiSet<E>) : AbstractMutableSet<MultiSet.Entry<E>>() {
        public override fun contains(element: MultiSet.Entry<E>): Boolean {
            val entry = element
            val element = entry.element
            return parent.getCount(element) == entry.count
        }

        public override fun iterator(): MutableIterator<MultiSet.Entry<E>> {
            return parent.createEntrySetIterator()
        }

        override fun add(element: MultiSet.Entry<E>): Boolean {
            TODO("Not yet implemented")
        }

         override fun remove(element: MultiSet.Entry<E>): Boolean {
            val entry = element
            val element = entry.element
            if (parent.contains(element)) {
                val count = parent.getCount(element)
                if (entry.count == count) {
                    parent.remove(element, count)
                    return true
                }
            }
            return false
        }

        override val size: Int
            get() = parent.uniqueElements()

    }

    /**
     * Inner class iterator for the MultiSet.
     */
    private class MultiSetIterator<E>(private val parent: AbstractMultiSet<E>) : MutableIterator<E> {
        private val entryIterator = parent.entrySet().iterator()
        private var current: MultiSet.Entry<E>? = null
        private var itemCount = 0
        private var canRemove = false

        /** {@inheritDoc}  */
        public override fun hasNext(): Boolean {
            return itemCount > 0 || entryIterator.hasNext()
        }

        /** {@inheritDoc}  */
        public override fun next(): E {
            if (itemCount == 0) {
                current = entryIterator.next()
                itemCount = current!!.count
            }
            canRemove = true
            itemCount--
            return current!!.element
        }

        /** {@inheritDoc}  */
        public override fun remove() {
            check(canRemove)
            val count = current!!.count
            if (count > 1) {
                parent.remove(current!!.element)
            } else {
                entryIterator.remove()
            }
            canRemove = false
        }
    }

    /**
     * Inner class UniqueSet.
     *
     * @param <E> the element type.
    </E> */
    protected class UniqueSet<E> (
        /** The parent multiset  */
        protected val parent: AbstractMultiSet<E>,
    ) : AbstractMutableSet<E>() {
        public override fun clear() {
            parent.clear()
        }

        public override fun contains(element: E): Boolean {
            return parent.contains(element)
        }

        public override fun containsAll(elements: Collection<E>): Boolean {
            return parent.containsAll(elements)
        }

        public override fun iterator(): MutableIterator<E> {
            return parent.createUniqueSetIterator()
        }

        override fun add(element: E): Boolean {
            TODO("Not yet implemented")
        }

        public override fun remove(element:E): Boolean {
            return parent.remove(element, parent.getCount(element)) != 0
        }

        override val size: Int
            get() = parent.uniqueElements()

    }

    /** View of the elements  */
    @Transient
    private var uniqueSet: MutableSet<E>? = null

    /** View of the entries  */
    @Transient
    private var entrySet: MutableSet<MultiSet.Entry<E>>? = null

    override fun add(element: E): Boolean {
        add(element, 1)
        return true
    }

    override fun add(element: E, occurrences: Int): Int {
        throw UnsupportedOperationException()
    }

    override fun retainAll(elements: Collection<E>): Boolean {
        TODO("Not yet implemented")
    }



    /**
     * Clears the multiset removing all elements from the entrySet.
     */
    public override fun clear() {
        val it = entrySet().iterator()
        while (it.hasNext()) {
            it.next()
            it.remove()
        }
    }

    /**
     * Determines if the multiset contains the given element.
     *
     * @param element the object to search for
     * @return true if the multiset contains the given element
     */
    public override fun contains(element: E): Boolean {
        return getCount(element) > 0
    }

    /**
     * Create a new view for the set of entries in this multiset.
     *
     * @return a view of the set of entries
     */
    protected fun createEntrySet(): MutableSet<MultiSet.Entry<E>> {
        return EntrySet(this)
    }

    /**
     * Creates an entry set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @return the entrySet iterator
     */
    protected abstract fun createEntrySetIterator(): MutableIterator<MultiSet.Entry<E>>

    /**
     * Create a new view for the set of unique elements in this multiset.
     *
     * @return a view of the set of unique elements
     */
    protected fun createUniqueSet(): MutableSet<E> {
        return UniqueSet(this)
    }

    /**
     * Creates a unique set iterator.
     * Subclasses can override this to return iterators with different properties.
     *
     * @return the uniqueSet iterator
     */
    protected open fun createUniqueSetIterator(): MutableIterator<E> {
        //val transformer: Transformer<MultiSet.Entry<E>, E> = MultiSet.Entry::element
        //return IteratorUtils.transformedIterator(entrySet().iterator(), transformer)
        TODO()
    }

    /**
     * Read the multiset in using a custom routine.
     * @param in the input stream
     * @throws IOException any of the usual I/O related exceptions
     * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
     * @throws ClassCastException if the stream does not contain the correct objects
     */
//    @Throws(IOException::class, java.lang.ClassNotFoundException::class)
//    protected fun doReadObject(`in`: ObjectInputStream) {
//        val entrySize: Int = `in`.readInt()
//        for (i in 0..<entrySize) {
//            val obj = `in`.readObject() as E?
//            val count: Int = `in`.readInt()
//            setCount(obj, count)
//        }
//    }

    /**
     * Write the multiset out using a custom routine.
     * @param out the output stream
     * @throws IOException any of the usual I/O related exceptions
     */
//    @Throws(IOException::class)
//    protected fun doWriteObject(out: ObjectOutputStream) {
//        out.writeInt(entrySet().size())
//        for (entry in entrySet()) {
//            out.writeObject(entry!!.element)
//            out.writeInt(entry.count)
//        }
//    }

    /**
     * Returns an unmodifiable view of the entries of this multiset.
     *
     * @return the set of entries in this multiset
     */
    override fun entrySet(): MutableSet<MultiSet.Entry<E>> {
        if (entrySet == null) {
            entrySet = createEntrySet()
        }
        return entrySet!!
    }

    override fun equals(other: Any?): Boolean {
        if (other === this) {
            return true
        }
        if (other !is MultiSet<*>) {
            return false
        }
        val other = other as MultiSet<E>
        if (other.size != size) {
            return false
        }
        for (entry in entrySet()) {
            if (other.getCount(entry.element) != getCount(entry.element)) {
                return false
            }
        }
        return true
    }

    /**
     * Returns the number of occurrence of the given element in this multiset by
     * iterating over its entrySet.
     *
     * @param element the object to search for
     * @return the number of occurrences of the object, zero if not found
     */
    override fun getCount(element: E): Int {
        for (entry in entrySet()) {
            val element1 = entry.element
            if (equals<Any?>(element1, element)) {
                return entry.count
            }
        }
        return 0
    }

    override fun hashCode(): Int {
        return entrySet().hashCode()
    }

    /**
     * Gets an iterator over the multiset elements. Elements present in the
     * MultiSet more than once will be returned repeatedly.
     *
     * @return the iterator
     */
    override fun iterator(): MutableIterator<E> {
        return MultiSetIterator<E>(this)
    }

    override fun remove(element: E): Boolean {
        return remove(element, 1) != 0
    }

    override fun remove(`object`: E, occurrences: Int): Int {
        throw UnsupportedOperationException()
    }

    override fun removeAll(elements: Collection<E>): Boolean {
        var result = false
        for (obj in elements) {
            val changed = remove(obj, getCount(obj)) != 0
            result = result || changed
        }
        return result
    }

    override fun setCount(element: E, count: Int): Int {
        require(count >= 0) { "Count must not be negative." }

        val oldCount = getCount(element)
        if (oldCount < count) {
            add(element, count - oldCount)
        } else {
            remove(element, oldCount - count)
        }
        return oldCount
    }

    /**
     * Returns the number of elements in this multiset.
     *
     * @return current size of the multiset
     */
    override val size: Int
        get() {
            var totalSize = 0
            for (entry in entrySet()) {
                totalSize += entry.count
            }
            return totalSize
        }

    /**
     * Implement a toString() method suitable for debugging.
     *
     * @return a debugging toString
     */
    override fun toString(): String {
        return entrySet().toString()
    }

    /**
     * Returns the number of unique elements in this multiset.
     *
     * @return the number of unique elements
     */
    protected abstract fun uniqueElements(): Int

    /**
     * Returns a view of the unique elements of this multiset.
     *
     * @return the set of unique elements in this multiset
     */
    override fun uniqueSet(): Set<E> {
        if (uniqueSet == null) {
            uniqueSet = createUniqueSet()
        }
        return uniqueSet!!
    }
}
